#include "lua_network.h"
#include "lua_utils.h"
#include "Win32Utils/CStrUtils.h"
#include "Win32Utils/CWinHttpClient.h"

static luaL_Reg lib_functions[] =
{
    {"get", lua_network::get},
    {"post", lua_network::post},
    {"download", lua_network::download},
    { NULL, NULL}
};

void lua_network::initialize(lua_State* L, const char* lib_name)
{
    lua_newtable(L);

    luaL_Reg* fun_info = lib_functions;
    while (fun_info->func)
    {
        lua_pushcfunction(L, fun_info->func);
        lua_setfield(L, -2, fun_info->name);
        fun_info++;
    }
    lua_setfield(L, -2, lib_name);
}

void lua_network::uninitialize()
{

}

int lua_network::get(lua_State* L)
{
    int nTop = lua_gettop(L);
    _tstring strUrl;
    std::string strParam;
    _tstring strHeader;
    bool fCallback = false;
    int ref = 0;

    if (lua_isfunction(L, -1))
    {
        fCallback = true;
        ref = luaL_ref(L, LUA_REGISTRYINDEX);
    }

    if (nTop >= 1 && lua_isstring(L, 1))
    {
        strUrl = lua_tostring(L, 1);
    }

    if (nTop >= 2)
    {
        if (lua_isstring(L, 2))
        {
            strParam = lua_tostring(L, 2);
        }
        else if (lua_istable(L, 2))
        {
            lua_utils::dump_value(L, strParam, 2, dump_type::dump_get);
        }
    }

    if (nTop >= 3)
    {
        if (lua_isstring(L, 3))
        {
            strHeader = lua_tostring(L, 3);
        }
        else if (lua_istable(L, 3))
        {
            lua_utils::dump_value(L, strHeader, 3, dump_type::dump_header);
        }
    }

    CWinHttpClient obj;

    CWinHttpResult res = obj.Get(strUrl, strParam, strHeader, [&L, ref, fCallback](const WINHTTP_PROGRESS_INFO& progress) -> bool {

        if (fCallback)
        {
            lua_rawgeti(L, LUA_REGISTRYINDEX, ref);

            lua_newtable(L);
            lua_utils::set_key_value(L, "progress", progress.lfProgress);
            lua_utils::set_key_value(L, "remainTime", progress.lfRemainTime);
            lua_utils::set_key_value(L, "speed", progress.lfSpeed);
            lua_utils::set_key_value(L, "cur", (double)progress.ullCur);
            lua_utils::set_key_value(L, "total", (double)progress.ullTotal);

            lua_pcall(L, 1, 0, 0);
        }

        return true;
        }
    );

    if (fCallback)
    {
        luaL_unref(L, LUA_REGISTRYINDEX, ref);
    }

    lua_newtable(L);

    lua_pushinteger(L, (lua_Integer)res.code);
    lua_setfield(L, -2, "code");

    lua_pushstring(L, res.result.c_str());
    lua_setfield(L, -2, "result");

    return 1;
}

int lua_network::post(lua_State* L)
{
    int nTop = lua_gettop(L);
    _tstring strUrl;
    std::string strParam;
    _tstring strHeader;
    bool fCallback = false;
    int ref = 0;

    if (lua_isfunction(L, -1))
    {
        fCallback = true;
        ref = luaL_ref(L, LUA_REGISTRYINDEX);
    }

    if (nTop >= 1 && lua_isstring(L, 1))
    {
        strUrl = lua_tostring(L, 1);
    }

    if (nTop >= 2)
    {
        if (lua_isstring(L, 2))
        {
            strParam = lua_tostring(L, 2);
        }
        else if (lua_istable(L, 2))
        {
            lua_utils::dump_value(L, strParam, 2, dump_type::dump_json);
        }
    }

    if (nTop >= 3)
    {
        if (lua_isstring(L, 3))
        {
            strHeader = lua_tostring(L, 3);
        }
        else if (lua_istable(L, 3))
        {
            lua_utils::dump_value(L, strHeader, 3, dump_type::dump_header);
        }
    }

    CWinHttpClient obj;

    CWinHttpResult res = obj.Post(strUrl, strParam, strHeader, [&L, ref, fCallback](const WINHTTP_PROGRESS_INFO& progress) -> bool {

        if (fCallback)
        {
            lua_rawgeti(L, LUA_REGISTRYINDEX, ref);

            lua_newtable(L);
            lua_utils::set_key_value(L, "Progress", progress.lfProgress);
            lua_utils::set_key_value(L, "RemainTime", progress.lfRemainTime);
            lua_utils::set_key_value(L, "Speed", progress.lfSpeed);
            lua_utils::set_key_value(L, "Cur", (double)progress.ullCur);
            lua_utils::set_key_value(L, "Total", (double)progress.ullTotal);

            lua_pcall(L, 1, 0, 0);
        }

        return true;
        }
    );

    if (fCallback)
    {
        luaL_unref(L, LUA_REGISTRYINDEX, ref);
    }

    lua_newtable(L);

    lua_utils::set_key_value(L, "code", (int)res.code);
    lua_utils::set_key_value(L, "result", res.result.c_str());

    return 1;
}

int lua_network::download(lua_State* L)
{
    int nTop = lua_gettop(L);
    _tstring strUrl;
    _tstring strHeader;
    _tstring strPath;
    bool fCallback = false;
    int ref = 0;

    if (lua_isfunction(L, -1))
    {
        fCallback = true;
        ref = luaL_ref(L, LUA_REGISTRYINDEX);
    }

    if (nTop >= 1 && lua_isstring(L, 1))
    {
        strUrl = CStrUtils::AStrToTStr(lua_tostring(L, 1));
    }

    if (nTop >= 2 && lua_isstring(L, 2))
    {
        strPath = CStrUtils::AStrToTStr(lua_tostring(L, 2));
    }

    if (nTop >= 3)
    {
        if (lua_isstring(L, 3))
        {
            strHeader = lua_tostring(L, 3);
        }
        else if (lua_istable(L, 3))
        {
            lua_utils::dump_value(L, strHeader, 3, dump_type::dump_header);
        }
    }

    CWinHttpClient obj;

    CWinHttpResult res = obj.DownloadFile(strUrl, strPath, strHeader, [&L, ref, fCallback](const WINHTTP_PROGRESS_INFO& progress) -> bool {

        if (fCallback)
        {
            lua_rawgeti(L, LUA_REGISTRYINDEX, ref);

            lua_newtable(L);
            lua_utils::set_key_value(L, "Progress", progress.lfProgress);
            lua_utils::set_key_value(L, "RemainTime", progress.lfRemainTime);
            lua_utils::set_key_value(L, "Speed", progress.lfSpeed);
            lua_utils::set_key_value(L, "Cur", (double)progress.ullCur);
            lua_utils::set_key_value(L, "Total", (double)progress.ullTotal);

            lua_pcall(L, 1, 0, 0);
        }

        return true;
    }
    , std::thread::hardware_concurrency());

    if (fCallback)
    {
        luaL_unref(L, LUA_REGISTRYINDEX, ref);
    }

    lua_newtable(L);
    lua_utils::set_key_value(L, "code", (int)res.code);
    lua_utils::set_key_value(L, "result", res.result.c_str());

    return 1;
}
